// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/i18n/message_formatter.h"

#include <memory>

#include "base/i18n/rtl.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/icu/source/common/unicode/unistr.h"
#include "third_party/icu/source/i18n/unicode/datefmt.h"
#include "third_party/icu/source/i18n/unicode/msgfmt.h"

typedef testing::Test MessageFormatterTest;

namespace base {
namespace i18n {

    class MessageFormatterTest : public testing::Test {
    protected:
        MessageFormatterTest()
        {
            original_locale_ = GetConfiguredLocale();
            SetICUDefaultLocale("en-US");
        }
        ~MessageFormatterTest() override
        {
            SetICUDefaultLocale(original_locale_);
        }

    private:
        std::string original_locale_;
    };

    namespace {

        void AppendFormattedDateTime(const std::unique_ptr<icu::DateFormat>& df,
            const Time& now,
            std::string* result)
        {
            icu::UnicodeString formatted;
            df->format(static_cast<UDate>(now.ToJsTime()), formatted).toUTF8String(*result);
        }

    } // namespace

    TEST_F(MessageFormatterTest, PluralNamedArgs)
    {
        const string16 pattern = ASCIIToUTF16(
            "{num_people, plural, "
            "=0 {I met nobody in {place}.}"
            "=1 {I met a person in {place}.}"
            "other {I met # people in {place}.}}");

        std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 0, "place", "Paris"));
        EXPECT_EQ("I met nobody in Paris.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 1, "place", "Paris"));
        EXPECT_EQ("I met a person in Paris.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 5, "place", "Paris"));
        EXPECT_EQ("I met 5 people in Paris.", result);
    }

    TEST_F(MessageFormatterTest, PluralNamedArgsWithOffset)
    {
        const string16 pattern = ASCIIToUTF16(
            "{num_people, plural, offset:1 "
            "=0 {I met nobody in {place}.}"
            "=1 {I met {person} in {place}.}"
            "=2 {I met {person} and one other person in {place}.}"
            "=13 {I met {person} and a dozen other people in {place}.}"
            "other {I met {person} and # other people in {place}.}}");

        std::string result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 0, "place", "Paris"));
        EXPECT_EQ("I met nobody in Paris.", result);
        // {person} is ignored if {num_people} is 0.
        result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 0, "place", "Paris", "person", "Peter"));
        EXPECT_EQ("I met nobody in Paris.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 1, "place", "Paris", "person", "Peter"));
        EXPECT_EQ("I met Peter in Paris.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 2, "place", "Paris", "person", "Peter"));
        EXPECT_EQ("I met Peter and one other person in Paris.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 13, "place", "Paris", "person", "Peter"));
        EXPECT_EQ("I met Peter and a dozen other people in Paris.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNamedArgs(
            pattern, "num_people", 50, "place", "Paris", "person", "Peter"));
        EXPECT_EQ("I met Peter and 49 other people in Paris.", result);
    }

    TEST_F(MessageFormatterTest, PluralNumberedArgs)
    {
        const string16 pattern = ASCIIToUTF16(
            "{1, plural, "
            "=1 {The cert for {0} expired yesterday.}"
            "=7 {The cert for {0} expired a week ago.}"
            "other {The cert for {0} expired # days ago.}}");

        std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "example.com", 1));
        EXPECT_EQ("The cert for example.com expired yesterday.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "example.com", 7));
        EXPECT_EQ("The cert for example.com expired a week ago.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "example.com", 15));
        EXPECT_EQ("The cert for example.com expired 15 days ago.", result);
    }

    TEST_F(MessageFormatterTest, PluralNumberedArgsWithDate)
    {
        const string16 pattern = ASCIIToUTF16(
            "{1, plural, "
            "=1 {The cert for {0} expired yesterday. Today is {2,date,full}}"
            "other {The cert for {0} expired # days ago. Today is {2,date,full}}}");

        base::Time now = base::Time::Now();
        using icu::DateFormat;
        std::unique_ptr<DateFormat> df(
            DateFormat::createDateInstance(DateFormat::FULL));
        std::string second_sentence = " Today is ";
        AppendFormattedDateTime(df, now, &second_sentence);

        std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "example.com", 1, now));
        EXPECT_EQ("The cert for example.com expired yesterday." + second_sentence,
            result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "example.com", 15, now));
        EXPECT_EQ("The cert for example.com expired 15 days ago." + second_sentence,
            result);
    }

    TEST_F(MessageFormatterTest, DateTimeAndNumber)
    {
        // Note that using 'mph' for all locales is not a good i18n practice.
        const string16 pattern = ASCIIToUTF16(
            "At {0,time, short} on {0,date, medium}, "
            "there was {1} at building {2,number,integer}. "
            "The speed of the wind was {3,number,###.#} mph.");

        using icu::DateFormat;
        std::unique_ptr<DateFormat> tf(
            DateFormat::createTimeInstance(DateFormat::SHORT));
        std::unique_ptr<DateFormat> df(
            DateFormat::createDateInstance(DateFormat::MEDIUM));

        base::Time now = base::Time::Now();
        std::string expected = "At ";
        AppendFormattedDateTime(tf, now, &expected);
        expected.append(" on ");
        AppendFormattedDateTime(df, now, &expected);
        expected.append(", there was an explosion at building 3. "
                        "The speed of the wind was 37.4 mph.");

        EXPECT_EQ(expected, UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(pattern, now, "an explosion", 3, 37.413)));
    }

    TEST_F(MessageFormatterTest, SelectorSingleOrMultiple)
    {
        const string16 pattern = ASCIIToUTF16(
            "{0, select,"
            "single {Select a file to upload.}"
            "multiple {Select files to upload.}"
            "other {UNUSED}}");

        std::string result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "single"));
        EXPECT_EQ("Select a file to upload.", result);
        result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "multiple"));
        EXPECT_EQ("Select files to upload.", result);

        // fallback if a parameter is not selectors specified in the message pattern.
        result = UTF16ToASCII(MessageFormatter::FormatWithNumberedArgs(
            pattern, "foobar"));
        EXPECT_EQ("UNUSED", result);
    }

} // namespace i18n
} // namespace base
